home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Personal Computer World 2009 February
/
PCWFEB09.iso
/
Software
/
Resources
/
Chat & Communication
/
PeerAware 1.03
/
PeerAware-Setup.exe
/
Html
/
scripts
/
abstractrenderer.js
next >
Wrap
Text File
|
2008-01-06
|
37KB
|
1,129 lines
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Cumulate Draw SVG Renderer.
*
* The Initial Developer of the Original Code is Cumulate Labs Inc.
* Portions created by Cumulate Labs Inc. are Copyright (C) 2006-2007
* Cumulate Labs Inc. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/**
*AbstractRenderer is the parent class for VMLRenderer and SVGRenderer. The intent is to provide
* a high level api for shape manipulation and hide the details in specific renderers.
**/
/**
* constructor, do not invoke this directly, used by renderers to subclass.
*/
function AbstractRenderer() {
};
AbstractRenderer.prototype.init = function(elem) {
this.VML_SVG_NAMESPACE="VML_SVG";
};
///////////////////////////////////////BEGIN OVERRIDDEN METHODS///////////////////////////////////////
/**
* return the bounds of the shape
* @see VMLRenderer.bounds
* @see SVGRenderer.bounds
*/
AbstractRenderer.prototype.bounds = function(shape) { return { x:0, y:0, width:0, height: 0 }; };
/**
* initial call to create a shape (but not connector, for connectors see createLine)
*/
AbstractRenderer.prototype.create = function(shape, fillColor, lineColor, lineWidth, left, top, width, height,opacity,gradient) {};
/**
* remove a node
*/
AbstractRenderer.prototype.remove = function(shape) {};
/**
* move a shape to new left and to location
*/
AbstractRenderer.prototype.move = function(shape, left, top) {};
AbstractRenderer.prototype.fineMove = function(shape, move,isHorizontal) {};
/**
* get the underlying shpae which cause this event
*/
AbstractRenderer.prototype.getShapeFromEventSource=function(source){};
/**
*get from x for a valid connector
**/
AbstractRenderer.prototype.getConnectorFromX=function(shape){}
/**
*get from y for a valid connector
**/
AbstractRenderer.prototype.getConnectorFromY=function(shape){}
/**
*get to x for a valid connector
**/
AbstractRenderer.prototype.getConnectorToX=function(shape){}
/**
*get to y for a valid connector
**/
AbstractRenderer.prototype.getConnectorToY=function(shape){}
/**
*set shape width
**/
AbstractRenderer.prototype.setWidth=function(shape,width){}
/**
*set shape height
**/
AbstractRenderer.prototype.setHeight=function(shape,height){}
/**
*set shape x (left)
**/
AbstractRenderer.prototype.setX=function(shape,left){}
/**
*set shape Y(top)
**/
AbstractRenderer.prototype.setY=function(shape,top){
}
/**
*get all shape objects (not including connectors)!
**/
AbstractRenderer.prototype.getAllShapes=function(doc){}
/**
*get all connectors (but not shapes)
**/
AbstractRenderer.prototype.getAllConnectors=function(doc){
}
/**
*Only used during draw, for general resize using handles see resizeWidth, resizeHeight
**/
AbstractRenderer.prototype.resize = function(shape, fromX, fromY, toX, toY) {};
/**
* @see vmlrenderer#editCommand
* @see svgrenderer#editCommand
*/
AbstractRenderer.prototype.editCommand = function(shape, cmd, value) {};
/**
* @see vmlrenderer#queryCommand
* @see svgrenderer#queryCommand
*
*/
AbstractRenderer.prototype.queryCommand = function(shape, cmd) {};
AbstractRenderer.prototype.showTracker = function(shape) {};
AbstractRenderer.prototype.updateTracker = function(shape) {};
AbstractRenderer.prototype.getMarkup = function() { return null; };
AbstractRenderer.prototype.fineRotateSelection=function(shape,angle){};
AbstractRenderer.prototype.rotate=function(shape,angle){};
AbstractRenderer.prototype.setCursor=function(shape,cursor){};
AbstractRenderer.prototype.setFillColor=function(shape,fillColor){};
AbstractRenderer.prototype.setShapeText=function(shape,text,font,override,zoom){};
AbstractRenderer.prototype.getShapeText=function(shape){};
AbstractRenderer.prototype.clearShapeText=function(shape){};
AbstractRenderer.prototype.sendToBack=function(shape){};
AbstractRenderer.prototype.bringToFront=function(shape){};
AbstractRenderer.prototype.updateZIndex=function(shape){};
AbstractRenderer.prototype.setLineStyle=function(connector,style){};
AbstractRenderer.prototype.getLineStyle=function(connector){};
AbstractRenderer.prototype.setStrokeWidth=function(shape,value){};
AbstractRenderer.prototype.getStrokeWidth=function(shape){};
AbstractRenderer.prototype.isConnector=function(shape){};
//line draw functions
AbstractRenderer.prototype.createLine=function(mode,lineColor,lineWidth,left,top,width,height){};
AbstractRenderer.prototype.moveLine=function(line,toX,toY,isFrom){};//isfrom indicates which end is being moved
AbstractRenderer.prototype.moveLinePoint=function(line,toX,toY,isFrom){};//isfrom indicates which end is being moved
AbstractRenderer.prototype.moveLineWithShape=function(shape){};
AbstractRenderer.prototype.showConnectionPoints=function(shape){};
AbstractRenderer.prototype.connectLine=function(shape,line,toOrFrom){};
/**
* this method is implemented only in the vml renderer to fix the polyline points issue in IE
**/
AbstractRenderer.prototype.savePolyLinePaths=function(){};
AbstractRenderer.prototype.appendPageAttribute=function(div,att){};
AbstractRenderer.prototype.getRealData=function(){};
AbstractRenderer.prototype.updateRotation=function(shape){}
/**
* return all the subshapes for a given shape
* @param shape
* @return list of all subshapes
*/
AbstractRenderer.prototype.getAllSubShapes=function(shape){};
/**
*this method will return the actual subject of a shape, the subject could either be a path or line,polyline in case
of connectors, this assumes that the shape has only one subject, if a shape is comples with multiple sub-shapes
* see getAllSubshapes.
**/
AbstractRenderer.prototype.getShapeSubject=function(shape){};
////////////////////////////////////END OVERRIDDEN FUNCTIONS//////////////////////////////////////////
/**
*resize shape width, this is a hacked method to compensate for shape movement during rotated resize. Ideally
* we should do some combination of translate rotate and translate back
* @param theshape
* @param current mouse X
* @param current mouse y
* @param initial mouse down x
* @param initia mouse down x
* @param prev width (before resize started)
* @param is right or left stretch
*
**/
AbstractRenderer.prototype.resizeWidth = function(shape,snappedX,snappedY, mouseDownX,mouseDownY,prevWidth,isRightStretch) {
var distanceA=((snappedX-mouseDownX)*(snappedX-mouseDownX))+((snappedY-mouseDownY)*(snappedY-mouseDownY));
distanceA=Math.sqrt(distanceA);
var bounds=this.bounds(shape);
var rotation=bounds.rotation;
if(!rotation||rotation=="undefined"){
rotation=0;
}
rotation =rotation%360;
var snpX=(snappedX*Math.cos((Math.PI/180)*(-rotation)))-(snappedY*Math.sin((Math.PI/180)*(-rotation)));
var mouseX=(mouseDownX*Math.cos((Math.PI/180)*(-rotation)))-(mouseDownY*Math.sin((Math.PI/180)*(-rotation)));
if(snpX<mouseX&&(isRightStretch)){
distanceA=-distanceA;
}
else if(snpX>mouseX&&(!isRightStretch)){
distanceA=-distanceA;
}
var width=prevWidth;
width=(width*1)+distanceA;
if(width<1){
return false;
}
var obj=null;
if(isRightStretch)
obj=this.getRotatedPoint(bounds.x,bounds.y,bounds);
else
obj=this.getRotatedPoint((bounds.x*1)+(bounds.width*1),(bounds.y*1)+(bounds.height*1),bounds);
this.setWidth(shape,width);
var newBounds=this.bounds(shape);
var newObj=null;
if(isRightStretch)
newObj=this.getRotatedPoint(newBounds.x,newBounds.y,newBounds);
else
newObj=this.getRotatedPoint((newBounds.x*1)+(newBounds.width*1),(newBounds.y*1)+(newBounds.height*1),newBounds);
var xFactor=Math.round(obj.x)-Math.round(newObj.x);
var yFactor=Math.round(obj.y)-Math.round(newObj.y);
this.setX(shape,(newBounds.x*1)+xFactor);
this.setY(shape,(newBounds.y*1)+yFactor);
this.updateRotation(shape);
};
/**
*resize height
* @param theshape
* @param current mouse X
* @param current mouse y
* @param initial mouse down x
* @param initia mouse down x
* @param prev height (before resize started)
* @param is top or bottom stretch
**/
AbstractRenderer.prototype.resizeHeight = function(shape, snappedX,snappedY, mouseDownX,mouseDownY,prevHeight,isBottomStretch) {
var distanceA=((snappedY-mouseDownY)*(snappedY-mouseDownY))+((snappedX-mouseDownX)*(snappedX-mouseDownX));
distanceA=Math.sqrt(distanceA);
var bounds=this.bounds(shape);
var rotation=bounds.rotation;
if(!rotation||rotation=="undefined"){
rotation=0;
}
rotation =rotation%360;
var snpY=(snappedY*Math.cos((Math.PI/180)*(-rotation)))+(snappedX*Math.sin((Math.PI/180)*(-rotation)));
var mouseY=(mouseDownY*Math.cos((Math.PI/180)*(-rotation)))+(mouseDownX*Math.sin((Math.PI/180)*(-rotation)));
if(snpY<mouseY&&isBottomStretch){
distanceA=-distanceA;
}
else if(snpY>mouseY&&(!isBottomStretch)){
distanceA=-distanceA;
}
var height=prevHeight;
height=(height*1)+distanceA;
if(height<1){
return false;
}
var obj=null;
if(isBottomStretch)
obj=this.getRotatedPoint(bounds.x,bounds.y,bounds);
else
obj=this.getRotatedPoint((bounds.x*1)+(bounds.width*1),(bounds.y*1)+(bounds.height*1),bounds);
this.setHeight(shape,height);
var newBounds=this.bounds(shape);
var newObj=null;
if(isBottomStretch)
newObj=this.getRotatedPoint(newBounds.x,newBounds.y,newBounds);
else
newObj=this.getRotatedPoint((newBounds.x*1)+(newBounds.width*1),(newBounds.y*1)+(newBounds.height*1),newBounds);
var xFactor=Math.round(obj.x)-Math.round(newObj.x);
var yFactor=Math.round(obj.y)-Math.round(newObj.y);
//alert("xFactor:"+xFactor+"yFactor:"+yFactor);
this.setX(shape,(newBounds.x*1)+xFactor);
this.setY(shape,(newBounds.y*1)+yFactor);
this.updateRotation(shape);
};
/**
* move the complete line segment, used for drag. Moves both ends of the line/polyline/curve and control points
* if any
* @param shape
* @param original shape bounds
* @param {int} deltaX
* @param {int} deltaY
*/
AbstractRenderer.prototype.moveCompleteLine=function(shape,bounds,deltaX,deltaY){
if(!this.isConnector(shape)){
return;
}
var type=bounds.type;
var fromX=(bounds.x*1)+deltaX;
var fromY=(bounds.y*1)+deltaY;
var toX=(bounds.x2*1)+deltaX;
var toY=(bounds.y2*1)+deltaY;
this.moveLine(shape,fromX,fromY,true);
this.moveLine(shape,toX,toY,false);
if(type=='curve-line'){
var controlX=(bounds.controlX*1)+deltaX;
var controlY=(bounds.controlY*1)+deltaY;
var controlX2=(bounds.controlX2*1)+deltaX;
var controlY2=(bounds.controlY2*1)+deltaY;
this.setControl1(shape,controlX,controlY);
this.setControl2(shape,controlX2,controlY2);
}
}
/**
* get the center point location for a connector, for line and ortholine:
* centerpoint=(x2+x1/2),(y2+y1/2)
* For curved connectors, the center point is (centerControlPoints+centerEndPoints)/2
* @return {object} with attributes x and y
*/
AbstractRenderer.prototype.getConnectorCenterPoint=function(shape){
var bounds=this.bounds(shape);
var type=this.getConnectorType(shape);
var obj=new Object();
if(type=='line'){
obj.x=((bounds.x*1)+(bounds.x2*1))/2;
obj.y=((bounds.y*1)+(bounds.y2*1))/2;
}
else if (type=='curve-line'){
var controlCenterX=((bounds.controlX*1)+(bounds.controlX2*1))/2;
var controlCenterY=((bounds.controlY*1)+(bounds.controlY2*1))/2;
var pointsCenterX=((bounds.x*1)+(bounds.x2*1))/2;
var pointsCenterY=((bounds.y*1)+(bounds.y2*1))/2;
obj.x=((controlCenterX*1)+(pointsCenterX))/2;
obj.y=((controlCenterY*1)+(pointsCenterY))/2;
}
else if (type=='ortho-line'){
var bounds=this.getOrthoLineCenterSegment(shape);
obj.x=((bounds.x*1)+(bounds.x2*1))/2;
obj.y=((bounds.y*1)+(bounds.y2*1))/2;
}
return obj;
}
/**
* TODO: should be renamed to getTextSize since it returns the height and width of any text, not just
* connector text
* @param theshape
* @param thetext
* @param current zoomfactor
* @param current font (if any)
*/
AbstractRenderer.prototype.getConnectorTextSize=function(shape,editText,zoom,oldFont){
var text=null;
var font=oldFont;
if(editText)
text=editText;
else
text=this.getShapeText(shape);
if(font==null){
font=this.getFont(shape);
}
//if no font, use default
if(font.size.length==0){
this.fillUpFont(font);
font.size=font.size*zoom;
}
//first calculate the number of lines
var numLines=1;
var last = 0;
while ( true ) {
last = text.indexOf("\n", last+1);
if ( last == -1 ) break;
numLines ++;
}
// if(numLines>1)numLines--;
//height will be numlines*2*font
var height=numLines*font.size*1.5;
//width will be the longest line +20
var lines=text.split("\n");
var highWidth=50;
for (var i=0;i<lines.length;i++){
if(lines[i].length*font.size/2>highWidth)
highWidth=lines[i].length*font.size/2;
}
highWidth=(highWidth*1)+10;
var obj=new Object();
obj.width=highWidth;
obj.height=height;
return obj;
}
AbstractRenderer.prototype.calculateOrthoLinePath=function(shape,fromX,fromY,toX,toY){
var points1=this.getClearancePoints(shape,fromX,fromY,toX,toY,true);
var points2=this.getClearancePoints(shape,toX,toY,points1[points1.length-1].x,points1[points1.length-1].y,false);
var x1=points1[points1.length-1].x;
var y1=points1[points1.length-1].y;
var x2=points2[points2.length-1].x;
var y2=points2[points2.length-1].y;
var obj=new Object();
obj.x=x1;
obj.y=y2;
points1[points1.length]=obj;
for(var i=points2.length-1;i>=0;i--){
points1[points1.length]=points2[i];
}
var path="";
for (var i=0;i<points1.length;i++){
path+=points1[i].x+",";
path+=points1[i].y+",";
}
return path;
}
/**
*get the clearance points for a given shape
**/
AbstractRenderer.prototype.getClearancePoints=function(line,fromX,fromY,toX,toY,isFrom){
//debugger;
var TOP=0;
var RIGHT=1;
var BOTTOM=2;
var LEFT=3;
var CONSTANT=12;
var array=new Array();
var obj=new Object();
obj.x=fromX;
obj.y=fromY;
array[array.length]=obj;
var shapeProps=this.getConnectionShape(line,isFrom);
if(!$(shapeProps.shapeid)){
return array;
}
var shape=$(shapeProps.shapeid);
var bounds=this.bounds(shape);
var loc=this.getConnectionPointLocation(shape,shapeProps.shapepoint,bounds);//implement
var clear=this.getRotatedClearancePoints(bounds,loc,CONSTANT);//implement,clear[0] will be first clearance, clear [1] will be second clearance
var orient=(parseInt((bounds.rotation)/90)+(loc*1))%4;
//add basic clearance
obj=new Object();
if(orient==TOP){
var factor=Math.abs(fromY-toY)/2;
if(factor<CONSTANT)factor=CONSTANT;
obj.y=array[array.length-1].y-CONSTANT;//replace this with constant!!!
obj.x=array[array.length-1].x;
}
else if(orient==BOTTOM){
var factor=Math.abs(fromY-toY)/2;
if(factor<CONSTANT)factor=CONSTANT;
obj.y=array[array.length-1].y+CONSTANT;//replace this with constant!!!
obj.x=array[array.length-1].x;
}
else if(orient==RIGHT){
var factor=Math.abs(fromX-toX)/2;
if(factor<CONSTANT)factor=CONSTANT;
obj.x=array[array.length-1].x+CONSTANT;//replace this with constant!!!
obj.y=array[array.length-1].y;
}
else if(orient==LEFT){
var factor=Math.abs(fromX-toX)/2;
if(factor<CONSTANT)factor=CONSTANT;
obj.x=array[array.length-1].x-CONSTANT;//replace this with constant!!!
obj.y=array[array.length-1].y;
}
array[array.length]=obj;
//now check for additional clearance
if(orient==TOP&&obj.y<toY){//target location is below, we need additional clearance
var nextObj=new Object();
if(toX<fromX){
nextObj.x=toX>((clear[0].x*1)-CONSTANT)?toX:((clear[0].x*1)-CONSTANT);
}
else{
nextObj.x=toX<((clear[1].x*1)+(CONSTANT*1))?toX:((clear[1].x*1)+(CONSTANT*1));
}
nextObj.y=obj.y;//fix later
array[array.length]=nextObj;
}
else if(orient==BOTTOM&&obj.y>toY){//target location is, we need additional clearance
var nextObj=new Object();
if(toX<fromX){
nextObj.x=toX>((clear[1].x*1)-(CONSTANT*1))?toX:((clear[1].x*1)-(CONSTANT*1));
}
else{
nextObj.x=toX<((clear[0].x*1)+(CONSTANT*1))?toX:((clear[0].x*1)+(CONSTANT*1));
}
nextObj.y=obj.y;//fix later
array[array.length]=nextObj;
}
else if(orient==RIGHT&&obj.x>toX){//target location is left, we need additional clearance
var nextObj=new Object();
if(toY<fromY){
nextObj.y=toY>((clear[0].y*1)-CONSTANT)?toY:((clear[0].y*1)-CONSTANT);
}
else{
nextObj.y=toY<((clear[1].y*1)+(CONSTANT*1))?toY:((clear[1].y*1)+(CONSTANT*1));
}
nextObj.x=obj.x;//fix later
array[array.length]=nextObj;
}
else if(orient==LEFT&&obj.x<toX){//target location is right, we need additional clearance
var nextObj=new Object();
if(toY<fromY){
nextObj.y=toY>((clear[1].y*1)-CONSTANT)?toY:((clear[1].y*1)-CONSTANT);
}
else{
nextObj.y=toY<((clear[0].y*1)+(CONSTANT*1))?toY:((clear[0].y*1)+(CONSTANT*1));
}
nextObj.x=obj.x;//fix later
array[array.length]=nextObj;
}
return array;
}
/**
* get the location of the point
*@param shape
*@param pointindex
*@param bounds
*@return orientation: 0:top,1:right,2:bottom,3:left
**/
AbstractRenderer.prototype.getConnectionPointLocation=function(shape,point,bounds){
var TOP=0;
var RIGHT=1;
var BOTTOM=2;
var LEFT=3;
var pointNode=shape.getElementsByTagName("connection-point")[point];
var x=this.getAttribute(pointNode,"x");
var y=this.getAttribute(pointNode,"y");
x=(bounds.x*1)+(bounds.width*(x/this.COORD_X));
y=(bounds.y*1)+(bounds.height*(y/this.COORD_Y));
if(x<(bounds.x*1+bounds.width/2))return LEFT;
else if(x>(bounds.x*1)+(bounds.width/2))return RIGHT;
else if(y<=bounds.y)return TOP;
else return BOTTOM;
}
/**
*return the rotated locaction of the shape clearances, clearance will vary by location
*@param {array}shape bounds
*@param {int}location 0:top,1:right,2:bottom,3:left
*@param {int}CONSTANT, how much to add to the clearance
**/
AbstractRenderer.prototype.getRotatedClearancePoints=function(bounds,loc,CONSTANT){
var TOP=0;
var RIGHT=1;
var BOTTOM=2;
var LEFT=3;
var array=new Array();
array[array.length]=new Object();
array[array.length]=new Object();
if(loc==TOP){
array[0].x=bounds.x;
array[0].y=bounds.y;
array[1].x=(bounds.x*1)+(bounds.width*1);
array[1].y=bounds.y;
}
else if(loc==RIGHT){
array[0].x=(bounds.x*1)+(bounds.width*1);
array[0].y=bounds.y;
array[1].x=(bounds.x*1)+(bounds.width*1);
array[1].y=(bounds.y*1)+(bounds.height*1);
}
else if(loc==BOTTOM){
array[0].x=(bounds.x*1)+(bounds.width*1);
array[0].y=(bounds.y*1)+(bounds.height*1);
array[1].x=(bounds.x*1);
array[1].y=(bounds.y*1)+(bounds.height*1);
}
else{
array[0].x=(bounds.x*1);
array[0].y=(bounds.y*1)+(bounds.height*1);
array[1].x=(bounds.x*1);
array[1].y=(bounds.y*1);
}
return array;//todo rotate these...
}
/**
*check if a line is connected,return the shape id and point
* @param theline(line/polyline/curve)
* @param isFrom --are we checking from or to end of the line
* @return {object} contains shapeid and the point number this line is connected to
**/
AbstractRenderer.prototype.getConnectionShape=function(line,isFrom){
var nodes=line.getElementsByTagName("connection");
var shapeid="";
var shapepoint="";
for(var i=0;i<nodes.length;i++){
var type=this.getAttribute(nodes[i],"type");
if(isFrom&&type=='from'){
shapeid=this.getAttribute(nodes[i],"shapeid");
shapepoint=this.getAttribute(nodes[i],"shapepoint");
}
else if(!isFrom&&type=='to'){
shapeid=this.getAttribute(nodes[i],"shapeid");
shapepoint=this.getAttribute(nodes[i],"shapepoint");
}
}
var obj=new Object();
obj.shapeid=shapeid;
obj.shapepoint=shapepoint;
return obj;
}
/**
*we assume that the shape has already moved,find the connectors attached to a shape and move them to match
* the current location of the shape connection point they are connected to.
* @param shape
**/
AbstractRenderer.prototype.moveLineWithShape=function(shape){
//debugger;
//first get all connections for this shape
var rect=this.bounds(shape);
var left=rect['x'];
var top=rect['y'];
var width=rect['width'];
var height=rect['height'];
var centerX=parseInt(left*1)+parseInt(width/2);
var centerY=parseInt(top*1)+parseInt(height/2);
//get the rotation
var rotation=rect['rotation'];
if(!rotation)rotation=0;
//get the shape coordinate size, hardcode for now
coordX=1000;
coordY=1000;
var connectList=shape.getElementsByTagName("connection");
for(var i=0;i<connectList.length;i++){
var connection=connectList.item(i);
var point=connection.parentNode;
var lineId=connection.getAttribute("lineid");
var toOrFrom=connection.getAttribute("type");
//for each connection point get its(unrotated) pixel location
var conX=(left*1)+((point.getAttribute("x")/this.COORD_X)*width);
var conY=(top*1)+((point.getAttribute("y")/this.COORD_Y)*height);
//now translate the center to the origin
conX=conX-centerX;
conY=conY-centerY;
//now rotate and translate back
var finalX=((conX*Math.cos((Math.PI/180)*(rotation)))-(conY*Math.sin((Math.PI/180)*(rotation)))*1)+(centerX*1);
var finalY=((conX*Math.sin((Math.PI/180)*(rotation)))+(conY*Math.cos((Math.PI/180)*(rotation)))*1)+(centerY*1);
if(toOrFrom=="to")
this.moveLine($(lineId),finalX,finalY,false);
else
this.moveLine($(lineId),finalX,finalY,true);
}
}
/*
* return an array of all the connectors attached to the shape
* @param shape
* @return array of connectors
*/
AbstractRenderer.prototype.getAllConnectorsForShape=function(shape){
if(!shape)return;
var connectList=shape.getElementsByTagName("connection");
var array=new Array();
for(var i=0;i<connectList.length;i++){
var connection=connectList.item(i);
var point=connection.parentNode;
var lineId=connection.getAttribute("lineid");
if($(lineId))array[array.length]=$(lineId);
}
return array;
}
/**
*add the connection attribute to the shape and the line
**/
AbstractRenderer.prototype.connectLineToShape=function(shape,line,toOrFrom,connectionPoint,pointIndex){
if(!shape||!line||(pointIndex<0))return;
var nodeList=line.getElementsByTagName("connection");
for(var i=0;i<nodeList.length;i++){
var node=nodeList.item(i);
var type=node.getAttribute("type");
if(type==toOrFrom){
node.setAttribute("shapeid",shape.id);
node.setAttribute("shapepoint",pointIndex);
var doc=this.container.ownerDocument;
var connection=this.createElement("c:connection","CUMULATE_LABS");
connection.setAttribute("lineid",line.id);
connection.setAttribute("type",toOrFrom);
connectionPoint.appendChild(connection);
}
}
}
/**
*disconnect the line i.e null out connection attributes in both the line and shape
**/
AbstractRenderer.prototype.disconnectLineFromShape=function(line,toOrFrom){
//debugger;
if(!line)return;
var nodeList=line.getElementsByTagName("connection");
for(var i=0;i<nodeList.length;i++){
var node=nodeList.item(i);
var type=node.getAttribute("type");
if(type==toOrFrom){
var shapeId=node.getAttribute("shapeid");
var point=node.getAttribute("shapepoint");
if(shapeId&&shapeId.length>0){
var shape=$(shapeId);
if(shape){
setHelp("Disconnected line from shape");
var connectionPoint=shape.getElementsByTagName("connection-point").item(point*1);
var connectList=connectionPoint.getElementsByTagName("connection");
for(var i=0;i<connectList.length;i++){
var connect=connectList.item(i);
if((connect.getAttribute("lineid")==line.id)&&(connect.getAttribute("type")==type)){
this.remove(connect);
break;
}
}
}
node.setAttribute("shapeid","xx");
node.setAttribute("shapepoint","-1");
}
}
}
}
/**
* given the shape bounds return an object with the centerx and y
* @return {object} with 2 attributes: centerX, centerY
*/
AbstractRenderer.prototype.getCenterPoint=function(bounds){
if(bounds){
var left=bounds.x;
var top=bounds.y;
var width=bounds.width;
var height=bounds.height;
var obj=new Object();
obj.centerX=(left*1)+(width/2);
obj.centerY=(top*1)+(height/2);
return obj;
}
}
/**
* get rotated point location. Given a point get its rotated location.
* @param {number}conX
* @param {number}conY
* @param bounds array with (x,y,width,height,rotation)
*/
AbstractRenderer.prototype.getRotatedPoint=function(conX,conY,bounds){
var rotation=bounds.rotation;
var center=this.getCenterPoint(bounds);
var centerX=center.centerX;
var centerY=center.centerY;
//now translate the center to the origin
conX=conX-centerX;
conY=conY-centerY;
//now rotate and translate back
var obj=new Object();
obj.x=((conX*Math.cos((Math.PI/180)*(rotation)))-(conY*Math.sin((Math.PI/180)*(rotation)))*1)+(centerX*1);
obj.y=((conX*Math.sin((Math.PI/180)*(rotation)))+(conY*Math.cos((Math.PI/180)*(rotation)))*1)+(centerY*1);
return obj;
}
/**
*To set the text of the shape, we have to do the following
*1. Get the text box bounds of the selected shape:
*2. Create a text set i.e break each line by new line token(<br/>) except for the first every text set is of type "newline"
*3. Calculate the distance between each line, see formula in handleTextPositioning
*5. Create line shapes for each text set using chars/line i.e divide each text set into line shapes and add to list,for the first
* line of each textset except new line, the type is "newline"
*6. Now start from the center of the shape list and draw everything above it and below it
**/
AbstractRenderer.prototype.setShapeText=function(shape,text,font,isSizeOverride,oldFont,zoomFactor/**zoom is only used for font**/){
var shapeFont=oldFont;
if(!shapeFont)
shapeFont=this.getFont(shape);
//now lets fill in whats not available
if(shapeFont.size==''||isSizeOverride){
shapeFont.size=font.size;
}
if(shapeFont.color==''){
shapeFont.color=font.color;
}
if(shapeFont.family==''){
shapeFont.family=font.family;
}
if(shapeFont.align==''){
shapeFont.align=font.align;
}
if(shapeFont.bold==''){
shapeFont.bold=font.bold;
}
if(shapeFont.italics==''){
shapeFont.italics=font.italics;
}
/**we need to preserve this so that whenever the shape is moved,it is used**/
var actualShapeFontSize=shapeFont.size;
var fontSize=shapeFont.size*zoomFactor;
shapeFont.size=fontSize;
var rect=this.getTextBounds(shape,text,zoomFactor,shapeFont);
var width=rect["width"];
var height=rect["height"];
var x=rect['x'];
var y=rect['y'];
if(width<25){
width=25;
}
if(height==0){
height=1;
}
//chars per line
var cpl=((width/fontSize))*2;
cpl=Math.ceil(cpl);
//wrap the text, false indicates--break the line if larger than cpl
var wrappedText=text.wordWrap(cpl,this.LINE_DELIMITER,false);
var textArray=this.createTextSet(wrappedText);
if(textArray.length==0){
return false;
}
var lines=this.createLines(textArray,cpl);
var lineCenter=Math.ceil(lines.length/2);
var shapeArray=null;
//ie has different algorithms for text line positioning for shapes and lines
if(this.isConnector(shape)){
shapeArray=this.handleConnectorTextLinePositioning(height,width,x,y,lineCenter,fontSize,lines,shapeFont,shape);
}
else{
shapeArray=this.handleTextLinePositioning(height,width,x,y,lineCenter,fontSize,lines,shapeFont,shape);
}
//finally create a dummy shape to put all the text in
//debugger;
var dummyLine=new Object();
dummyLine.type=shape.id;
dummyLine.text="";
shapeFont.size=actualShapeFontSize;
shapeArray[shapeArray.length]=this.createTextShape(dummyLine,false,0,0,0,0,shapeFont,shape);
if(this.isConnector(shape)){
this.handleConnectorTextBackgroundPositioning(shape,rect,text);
}
//now add all the shapes to the shape
for(var i=0;i<shapeArray.length;i++){
shape.appendChild(shapeArray[i]);
}
var textData=this.setTextData(shape,text);
}
/**
* create the background rect element, this is the background shape for connector text
* @param the connector shape
*/
AbstractRenderer.prototype.getConnectorBackgroundShape=function(shape){
var bgShape=null;
if(shape.getElementsByTagName("rect").length>0)
bgShape=shape.getElementsByTagName("rect")[0];
else{
bgShape=this.createElement("rect",this.VML_SVG_NAMESPACE);
bgShape.style.position='absolute';
this.setFillColor(shape,"white");
this.setOpacity(shape,"1.0");
var subject=this.getShapeSubject(shape);
bgShape.style.zIndex=subject.style.zIndex;
//else
//bgShape.style.zIndex=this.maxIndex;
this.setStrokeWidth(bgShape,"0px");
}
return bgShape;
}
/**
* starting version 0.3.7, the text will be stored in a comment field inside the c:textData element, this is done
* for optimization purposes and because newlines were being lost by placing the text in attributes, In
* addition the text will be htmlEncoded to prevent invalid xml
*/
AbstractRenderer.prototype.setTextData=function(shape,text){
if(!text)text="";
//first clean up existing nodes
var nodes=shape.getElementsByTagName("textData");
var nodeArray=$A(nodes);
for(var i=0;i<nodeArray.length;i++){
var node=nodeArray[i];
this.remove(node);
}
var textData=this.createElement("c:textData","CUMULATE_LABS");
var textNode=this.container.ownerDocument.createComment(JH.Utilities.HtmlEncode2(text));
textData.appendChild(textNode);
textData.setAttribute("version","0.3.7");
shape.appendChild(textData);
}
/**
* starting version 0.3.6, the text will be stored in a comment field inside the c:textData element, this is done
* for optimization purposes and because newlines were being lost by placing the text in attributes
* Update:since 0.3.7 we are placing encoded html text instead of just escaped one...
*/
AbstractRenderer.prototype.getShapeText=function (shape,otherDoc){
//this is pre 0.3.2, we will eventually remove this
var element=document.getElementById("text:dummy"+shape.id);
//for pre 0.3.6
if(element){
return this.getShapeTextDeprecated(shape,otherDoc);
}
var nodes=shape.getElementsByTagName("textData");
var version=null;
if(nodes.length>0)
version=nodes[0].getAttribute("version");
if(nodes.length==0||version==null||version==""){
return this.getShapeTextDeprecated(shape,otherDoc);
}
else {
var comment=nodes[0].firstChild;
if(comment==null)return "";
var text=comment.nodeValue;
if(version=="0.3.6"){
text=unescape(text);
}
else{//since 0.3.7 we are placing encoded html text instead of just escaped one...
text=JH.Utilities.HtmlDecode2(text);
}
return text;
}
}
/**
*this method will return the actual text of the shape which has been hidden in a dummy shape
*/
AbstractRenderer.prototype.getShapeTextDeprecated=function(shape,otherDoc){
if(!shape){
return null;
}
//we keep this for backward compatibility...we are not going to use this
//starting 0.3, we use c:textData elements to store data
var element=document.getElementById("text:dummy"+shape.id);
if(element) return element.string;
//this is how post 0.2 implementations will handle text data
else{
var string="";
var nodes=shape.getElementsByTagName("textData");
for(var i=0;i<nodes.length;i++){
string+=nodes[i].getAttribute("string");
}
return string;
}
}
/**
*create the lines
*@param array of lines
*@param characters for line
*/
AbstractRenderer.prototype.createLines=function(textArray,charsPerLine){
var lineArray=new Array();
for(var i=0;i<textArray.length;i++){
var stringVal=textArray[i];
if(stringVal.length<charsPerLine){
var lineObj=new Object();
lineObj.text=stringVal;
lineObj.type="normal";
lineArray[lineArray.length]=lineObj;
}
else{
var numlines=Math.floor((stringVal.length/charsPerLine));
var remainder=stringVal.length%charsPerLine;
var j=0;
for(j=0;j<numlines;j++){
var line=stringVal.substr((j*charsPerLine),charsPerLine);
var lineObj=new Object();
lineObj.text=line;
if(j==0){
lineObj.type="newline";
}
else{
lineObj.type="normal";
}
lineArray[lineArray.length]=lineObj;
}
if(remainder>0){
var line=stringVal.substr((j*charsPerLine),remainder);
var lineObj=new Object();
lineObj.text=line;
if(numlines==0){
lineObj.type="newline";
}
else{
lineObj.type="normal";
}
lineArray[lineArray.length]=lineObj;
}
}
}
return lineArray;
}
/**
* for shapes, return the text bounds if it exists, otherwise return the dimensions of the shape. For connectors
* return the text width and height (or reasonable default) anchored around the center point
* @param shape
* @param connectorText (optional)--only passed for connectors
* @param current zoom factor
*/
AbstractRenderer.prototype.getTextBounds=function(shape, connectorText,zoomFactor,font){
if(this.isConnector(shape)){
var center=this.getConnectorCenterPoint(shape);
var textSize=this.getConnectorTextSize(shape,connectorText,zoomFactor,font);
var bounds=new Object();
bounds.width=textSize.width;
bounds.height=textSize.height;
bounds.x=(center.x-bounds.width/2);
bounds.y=(center.y-bounds.height/2);
return bounds;
}
else{
var rect=this.bounds(shape);
var textBounds=shape.getElementsByTagName("text-bound");
if(textBounds.length==0)return rect;
else{
var bounds=rect;
var textb=textBounds[0];
var x=this.getAttribute(textb,"fromX");
var y=this.getAttribute(textb,"fromY");
var x2=this.getAttribute(textb,"toX");
var y2=this.getAttribute(textb,"toY");
var newX=(x*bounds.width/1000)+(bounds.x*1);
var newY=(y*bounds.height/1000)+(bounds.y*1);
var width=(x2-x)*(bounds.width/1000);
var height=(y2-y)*(bounds.height/1000)
bounds.x=bounds.left=newX;
bounds.y=bounds.top=newY;
var textSize=this.getConnectorTextSize(shape,connectorText,zoomFactor,font);
bounds.width=textSize.width;
bounds.height=textSize.height;
return bounds;
}
}
}
/**
*break the text up by new lines \n, called from setShapeText
*/
AbstractRenderer.prototype.createTextSet=function(text){
var textArray=new Array();
if(!text||text.length==0){
return textArray;
}
else{
textArray=text.split(this.LINE_DELIMITER);
return textArray;
}
}
/**
*Intialize an empty font object with the remaining default attributes (besides the one already populated)
* this way we don't have to poulate defaults everywhere a font is used.
* @param a partially filled font object
**/
AbstractRenderer.prototype.fillUpFont=function(font){
if(font.size=='')
font.size=16;
if(font.family=='')
font.family="'arial'";
if(font.italics=='')
font.italics="normal";
if(font.bold=='')
font.bold="normal"
if(font.align=='')
font.align="center";
if(font.color=='')
font.color="black";
}